package org.ccay.log.sqltrace.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.IUserPrincipal;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.request.impl.RequestContextManager;
import org.ccay.core.util.NetSecurityUtil;
import org.ccay.log.SqlTraceVO;

/**
 * SQL Trace 拦截器
 * 
 * @author chaowangbang
 * 
 */
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class SqlTraceInterceptor implements Interceptor {
	private static final ILogger log = CcayLoggerFactory.getLogger(SqlTraceInterceptor.class);

	/*
	 * 对执行时的sql进行拦截
	 * 
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin
	 * .Invocation)
	 */
	public Object intercept(Invocation invocation) throws Throwable {

		if (!SqlTraceStatusManager.isSqlTraceEnabled()) {
			return invocation.proceed();
		}

		// 获取请求上下文
		RequestContext context = (RequestContext) RequestContextManager.getCurrent(true);
		if (context == null || context.getUser() == null || SqlTraceStatusManager.getSqlTraceVOMap().get(context.getUser().getUserName()) == null) {
			log.debug("context or context.getUser() is null");
			return invocation.proceed();
		}
		// 获取当前用户待跟踪的信息
		 IUserPrincipal user = context.getUser();
		SqlTraceVO vo = SqlTraceStatusManager.getSqlTraceVOMap().get(user.getUserName());

		Executor executor = (Executor) invocation.getTarget();
		Connection connection = executor.getTransaction().getConnection();

		String traceFileIdentifier = NetSecurityUtil.getValidSQLPara(vo.getTraceFileIdentifier());
		String maxDumpFileSize = NetSecurityUtil.getValidSQLPara(vo.getMaxDumpFileSize());

		startTrace(vo, connection, traceFileIdentifier, maxDumpFileSize);
		try {
			return invocation.proceed();
		} catch (RuntimeException e) {
			log.error(e);
			throw e;
		} finally {
			stopTrace(connection, traceFileIdentifier, maxDumpFileSize);
		}
	}

	/**
	 * 停止SQL Trace 的跟踪
	 * 
	 * @param connection
	 * @param traceFileIdentifier
	 * @param maxDumpFileSize
	 */
	private void stopTrace(Connection connection, String traceFileIdentifier, String maxDumpFileSize) {
		// 关闭SQL Trace、statement
		PreparedStatement psClose = null;
		try {
			String sql = MessageFormat.format("ALTER SESSION SET TRACEFILE_IDENTIFIER =''{0}'' max_dump_file_size=''{1}''  EVENTS= ''10046 TRACE NAME CONTEXT off'' ", traceFileIdentifier,
					maxDumpFileSize);
			log.debug("Stop trace sql is:" + sql);
			psClose = connection.prepareStatement(sql);
			psClose.executeUpdate();
		} catch (SQLException e) {
			log.error(e);
		} finally {
			if (psClose != null) {
				try {
					psClose.close();
				} catch (SQLException e) {
					log.error(e);
				}
			}
		}
	}

	/**
	 * 启动SQL Trace 跟踪
	 * 
	 * @param invocation
	 * @param vo
	 * @param connection
	 * @param traceFileIdentifier
	 * @param maxDumpFileSize
	 */
	private void startTrace(SqlTraceVO vo, Connection connection, String traceFileIdentifier, String maxDumpFileSize) {
		PreparedStatement psOpen = null;
		try {
			String sql = MessageFormat.format("ALTER SESSION SET TRACEFILE_IDENTIFIER =''{0}'' max_dump_file_size=''{1}'' EVENTS=''10046 TRACE NAME CONTEXT FOREVER, LEVEL {2,number}''",
					traceFileIdentifier, maxDumpFileSize, vo.getSqlTraceLevel());
			log.debug("Start trace sql is:" + sql);
			psOpen = connection.prepareStatement(sql);
			psOpen.executeUpdate();
		} catch (SQLException e) {
			log.error(e);
		} finally {
			if (psOpen != null) {
				try {
					psOpen.close();
				} catch (SQLException e) {
					log.error(e);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
	 */
	public void setProperties(Properties arg0) {

	}
}
